﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using log4net;
using log4net.Config;

namespace Mayfair.Core.Common.Util
{
    public static class Logger
    {
        private static readonly string CONFIG_FILE_NAME = "Log4Net.config";
        // 同期オブジェクト
        private static readonly object locker = new object();
        private static readonly Dictionary<Type, ILog> loggers = new Dictionary<Type, ILog>();
        // Log初期化判断
        private static bool logInitialized;

        /// <summary>
        /// Privateメソッド(SerializeException)をラッパー
        /// </summary>
        /// <param name="e">例外情報</param>
        /// <returns>例外ログ出力文字列</returns>
        [DebuggerStepThrough()]
        public static string SerializeException(Exception e)
        {
            return SerializeException(e, string.Empty);
        }

        /// <summary>
        /// 例外情報を文字列として返却
        /// </summary>
        /// <param name="e">例外情報</param>
        /// <param name="exceptionMessage">例外メッセージ</param>
        /// <returns>例外ログ出力文字列</returns>
        [DebuggerStepThrough()]
        private static string SerializeException(Exception e, string exceptionMessage)
        {
            // Exceptionがnullの際には、String.Emptyを返却
            if (e == null) return string.Empty;

            // Log出力対象として、MessageとStackTraceを採用
            exceptionMessage = string.Format(
                "{0}{1}{2}\n{3}",
                exceptionMessage,
                (exceptionMessage == string.Empty) ? string.Empty : "\n\n",
                e.Message,
                e.StackTrace);

            return exceptionMessage;
        }

        /// <summary>
        /// Loggerオブジェクトを取得
        /// </summary>
        /// <param name="source">対象オブジェクトの型</param>
        /// <returns>ILog</returns>
        [DebuggerStepThrough()]
        private static ILog GetLogger(Type source)
        {
            lock (locker)
            {
                // 対象のオブジェクトの型を確認
                if (loggers.ContainsKey(source))
                {
                    return loggers[source];
                }
                ILog logger = LogManager.GetLogger(source);
                loggers.Add(source, logger);
                return logger;
            }
        }

        #region"Log a message object"

        [DebuggerStepThrough()]
        public static void Debug(object source, object message)
        {
            Debug(source.GetType(), message);
        }

        [DebuggerStepThrough()]
        public static void Debug(Type source, object message)
        {
            GetLogger(source).Debug(message);
        }

        [DebuggerStepThrough()]
        public static void Info(object source, object message)
        {
            Info(source.GetType(), message);
        }

        [DebuggerStepThrough()]
        public static void Info(Type source, object message)
        {
            GetLogger(source).Info(message);
        }

        [DebuggerStepThrough()]
        public static void Warn(object source, object message)
        {
            Warn(source.GetType(), message);
        }

        [DebuggerStepThrough()]
        public static void Warn(Type source, object message)
        {
            GetLogger(source).Warn(message);
        }

        [DebuggerStepThrough()]
        public static void Error(object source, object message)
        {
            Error(source.GetType(), message);
        }

        [DebuggerStepThrough()]
        public static void Error(Type source, object message)
        {
            GetLogger(source).Error(message);
        }

        [DebuggerStepThrough()]
        public static void Fatal(object source, object message)
        {
            Fatal(source.GetType(), message);
        }

        [DebuggerStepThrough()]
        public static void Fatal(Type source, object message)
        {
            GetLogger(source).Fatal(message);
        }

        #endregion

        #region"Log a message object & exception"

        [DebuggerStepThrough()]
        public static void Debug(object source, object message, Exception exception)
        {
            Debug(source.GetType(), message, exception);
        }

        [DebuggerStepThrough()]
        public static void Debug(Type source, object message, Exception exception)
        {
            GetLogger(source).Debug(message, exception);
        }

        [DebuggerStepThrough()]
        public static void Info(object source, object message, Exception exception)
        {
            Info(source.GetType(), message, exception);
        }

        [DebuggerStepThrough()]
        public static void Info(Type source, object message, Exception exception)
        {
            GetLogger(source).Info(message, exception);
        }

        [DebuggerStepThrough()]
        public static void Warn(object source, object message, Exception exception)
        {
            Warn(source.GetType(), message, exception);
        }

        [DebuggerStepThrough()]
        public static void Warn(Type source, object message, Exception exception)
        {
            GetLogger(source).Warn(message, exception);
        }

        [DebuggerStepThrough()]
        public static void Error(object source, object message, Exception exception)
        {
            Error(source.GetType(), message, exception);
        }

        [DebuggerStepThrough()]
        public static void Error(Type source, object message, Exception exception)
        {
            GetLogger(source).Error(message, exception);
        }

        [DebuggerStepThrough()]
        public static void Fatal(object source, object message, Exception exception)
        {
            Fatal(source.GetType(), message, exception);
        }

        [DebuggerStepThrough()]
        public static void Fatal(Type source, object message, Exception exception)
        {
            GetLogger(source).Fatal(message, exception);
        }

        #endregion

        /// <summary>
        /// 設定ファイルを初期化
        /// </summary>
        [DebuggerStepThrough()]
        private static void Initialize()
        {
            XmlConfigurator.ConfigureAndWatch(new FileInfo(GetConfigFilePath()));
        }

        /// <summary>
        /// 設定ファイルパスを取得
        /// </summary>
        /// <returns>設定ファイルパス</returns>
        [DebuggerStepThrough()]
        private static string GetConfigFilePath()
        {
            string basePath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
            string configPath = Path.Combine(basePath, CONFIG_FILE_NAME);

            if (!File.Exists(configPath))
            {
                configPath = Path.Combine(basePath, "bin");
                configPath = Path.Combine(configPath, CONFIG_FILE_NAME);

                if (!File.Exists(configPath))
                {
                    configPath = Path.Combine(basePath, @"..\" + CONFIG_FILE_NAME);
                }
            }
            return configPath;
        }

        /// <summary>
        /// Log4Net設定情報初期化
        /// </summary>
        [DebuggerStepThrough()]
        public static void EnsureInitialized()
        {
            if (!logInitialized)
            {
                Initialize();
                logInitialized = true;
            }
        }

        /// <summary>
        /// Log4Netで保持しているAppendersを解放 
        /// </summary>
        [DebuggerStepThrough()]
        public static void Shutdown()
        {
            LogManager.Shutdown();
        }
    }
}